/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.editor.controller;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.io.IOException;

import javax.swing.Action;
import javax.swing.JEditorPane;
import javax.swing.KeyStroke;
import javax.swing.text.BadLocationException;

import offset.nodes.client.editor.Editor;
import offset.nodes.client.editor.model.Range;
import offset.nodes.client.editor.model.TableModel;
import offset.nodes.client.editor.view.InsertTableDialog;

/**
 *
 * @author Walter Lütgenau
 */
public class TableActions {

    public static abstract class AbstractTableAction extends EditorActions.AbstractEditorAction {

        public AbstractTableAction(String name, Editor editor) {
            super(name, editor);
        }

        protected TableModel getModel(ActionEvent ae) {
            return new TableModel(getDocumentContext(ae));
        }
    }

    public static class InsertTableAction extends AbstractTableAction {

        public void action(ActionEvent e) throws Exception {
            InsertTableDialog dialog = new InsertTableDialog(null, true);
            dialog.setLocation(50, 50);
            TableModel model = getModel(e);
            JEditorPane pane = getEditor(e);

            if (dialog.showDialog() == InsertTableDialog.OK) {
                int pos = pane.getCaretPosition();

                Range range = model.insertTable(pos, dialog.getRowCount(), dialog.getColumnCount(),
                        dialog.getColumnWidthType(), pane.getWidth(), dialog.headerLabels(), dialog.columnLabels());

                pane.setSelectionStart(range.getStartOffset());
                pane.setSelectionEnd(range.getEndOffset());
            }
        }

        public InsertTableAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Insert_..."), editor);
            setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.SHIFT_MASK | java.awt.event.InputEvent.CTRL_MASK));
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc_inserttable.png")));
        }
    }

    public static class TableTabAction extends AbstractTableAction {

        public void action(ActionEvent e) throws Exception {
            TableModel model = getModel(e);
            int pos = getEditor(e).getCaretPosition();

            if (model.isEndOfTable(pos)) {
                Range range = model.addRowAfter(pos);
                getEditor(e).setCaretPosition(pos + 1);

                getEditor(e).setSelectionStart(range.getStartOffset());
                getEditor(e).setSelectionEnd(range.getEndOffset());
            } else {
                Range range = model.nextCell(pos);
                getEditor(e).setCaretPosition(pos + 1);

                getEditor(e).setSelectionStart(range.getStartOffset());
                getEditor(e).setSelectionEnd(range.getEndOffset());
            }
        }

        public TableTabAction(Editor editor) {
            super("", editor);
        }
    }

    public static class TableBackTabAction extends AbstractTableAction {

        public void action(ActionEvent e) throws Exception {
            TableModel model = getModel(e);
            int pos = getEditor(e).getCaretPosition();

            Range range = model.previousCell(pos);
            if (range != null) {
                getEditor(e).setCaretPosition(pos + 1);

                getEditor(e).setSelectionStart(range.getStartOffset());
                getEditor(e).setSelectionEnd(range.getEndOffset());
            }
        }

        public TableBackTabAction(Editor editor) {
            super("", editor);
        }
    }

    public static class UpdateColumnWidthsAction extends AbstractTableAction {

        int pos;
        int startX;
        int endX;
        int newEndX;

        public void action(ActionEvent e) throws Exception {
            int pos = getEditor(e).getCaretPosition();
            TableModel model = getModel(e);
            model.updateColumnWidth(pos, startX, endX, newEndX);
            model.reset(pos); // for unknown reasons, a left drag does not get repainted, neither normally or in undo mode. So we take drastic measures!
            getEditor(e).setCaretPosition(pos);
        }

        public UpdateColumnWidthsAction(int pos, int startX, int endX, int newEndX, Editor editor) {
            super("", editor);
            this.pos = pos;
            this.startX = startX;
            this.endX = endX;
            this.newEndX = newEndX;
        }
    }

    public static class InsertRowBelowAction extends AbstractTableAction {

        public void action(ActionEvent e) throws Exception {
            getModel(e).insertRowBelow(getEditor(e).getCaretPosition());
        }

        public InsertRowBelowAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Below"), editor);
            setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_R, java.awt.event.InputEvent.CTRL_MASK));
        }
    }

    public static class InsertRowAboveAction extends AbstractTableAction {

        public void action(ActionEvent e) throws Exception {
            getModel(e).insertRowAbove(getEditor(e).getCaretPosition());
        }

        public InsertRowAboveAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Above"), editor);
        }
    }

    public static class DeleteRowAction extends AbstractTableAction {

        public void action(ActionEvent e) throws Exception {
            getModel(e).deleteRow(getEditor(e).getCaretPosition());
        }

        public DeleteRowAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Delete_Row"), editor);
            setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_R, java.awt.event.InputEvent.SHIFT_MASK | java.awt.event.InputEvent.CTRL_MASK));
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc_deleterows.png")));
        }
    }

    public static class InsertColumnRightAction extends AbstractTableAction {

        public void action(ActionEvent e) throws Exception {
            getModel(e).insertColumnRight(getEditor(e).getCaretPosition());
        }

        public InsertColumnRightAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Right"), editor);
            setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_L, java.awt.event.InputEvent.CTRL_MASK));
        }
    }

    public static class InsertColumnLeftAction extends AbstractTableAction {

        public void action(ActionEvent e) throws Exception {
            getModel(e).insertColumnLeft(getEditor(e).getCaretPosition());
        }

        public InsertColumnLeftAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Left"), editor);
        }
    }

    public static class DeleteColumnAction extends AbstractTableAction {

        public void action(ActionEvent e) throws Exception {
            getModel(e).deleteColumn(getEditor(e).getCaretPosition());
        }

        public DeleteColumnAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Delete_Column"), editor);
            setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_L, java.awt.event.InputEvent.SHIFT_MASK | java.awt.event.InputEvent.CTRL_MASK));
            setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc_deletecolumns.png")));
        }
    }

    public static class TableDeletePrevCharAction extends AbstractTableAction {

        Action defaultAction;

        public void action(ActionEvent e) throws Exception {
            TableModel model = getModel(e);
            JEditorPane pane = getEditor(e);
            int pos = pane.getCaretPosition();

            if (model.getStartOfCell(pos) == pos) {
                if (pos > 0) {
                    pane.setCaretPosition(pos - 1);
                }
            } else {
                defaultAction.actionPerformed(e);
            }
        }

        public TableDeletePrevCharAction(Action defaultAction, Editor editor) {
            super("", editor);
            this.defaultAction = defaultAction;
        }
    }

    public static class TableCaretUpAction extends AbstractTableAction {

        Action defaultAction;

        public void action(ActionEvent e) throws Exception {
            TableModel model = getModel(e);
            JEditorPane pane = getEditor(e);
            int pos = pane.getCaretPosition();

            Rectangle r = pane.getUI().modelToView(pane, pos);
            double y = r.getY();
            while (r.getY() == y || (model.isTable(pane.getCaretPosition()) && !model.isSameColumn(pos, pane.getCaretPosition()))) {
                defaultAction.actionPerformed(e);
                r = pane.getUI().modelToView(pane, pane.getCaretPosition());
            }
        }

        public TableCaretUpAction(Action defaultAction, Editor editor) {
            super("", editor);
            this.defaultAction = defaultAction;
        }
    }

    public static class TableCaretDownAction extends AbstractTableAction {

        Action defaultAction;

        public void action(ActionEvent e) throws Exception {
            TableModel model = getModel(e);
            JEditorPane pane = getEditor(e);
            int pos = pane.getCaretPosition();

            Rectangle r = pane.getUI().modelToView(pane, pos);
            double y = r.getY();
            while (r.getY() == y || (model.isTable(pane.getCaretPosition()) && !model.isSameColumn(pos, pane.getCaretPosition()))) {
                defaultAction.actionPerformed(e);
                r = pane.getUI().modelToView(pane, pane.getCaretPosition());
            }
        }

        public TableCaretDownAction(Action defaultAction, Editor editor) {
            super("", editor);
            this.defaultAction = defaultAction;
        }
    }

    public static class TableCaretEnterAction extends AbstractTableAction {

        Action defaultAction;

        protected boolean isInCell(JEditorPane pane, TableModel model, int cellPos, int x) throws BadLocationException, IOException {
            Range cellRange = model.getCurrentCellRange(cellPos);
            Rectangle rStart = pane.getUI().modelToView(pane, cellRange.getStartOffset());
            Rectangle rEnd = pane.getUI().modelToView(pane, cellRange.getEndOffset());

            return x >= rStart.x && x <= rEnd.x + rEnd.width;
        }

        public void action(ActionEvent e) throws Exception {
            TableModel model = getModel(e);
            JEditorPane pane = getEditor(e);
            int pos = pane.getCaretPosition();
            Point magic = pane.getCaret().getMagicCaretPosition();

            while (!isInCell(pane, model, pane.getCaretPosition(), magic.x)) {
                defaultAction.actionPerformed(e);
            }
        }

        public TableCaretEnterAction(Action defaultAction, Editor editor) {
            super("", editor);
            this.defaultAction = defaultAction;
        }
    }
}
